﻿using DataSync.Entity;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;

namespace Boco.CommonLib
{
    public class FtpClient
    {
        private string remotePath = "/";
        private static object _lock = new object();
        public string FileMatch
        {
            get;
            set;
        }
        FtpConfig Config
        {
            get;
            set;
        }
        public string Host
        {
            get;
            set;
        }
        public string UserId
        {
            get;
            set;
        }
        public string Password
        {
            get;
            set;
        }
        public IWebProxy Proxy
        {
            get;
            set;
        }
        public int Port
        {
            get;
            set;
        }
        public bool EnableSsl
        {
            get;
            set;
        }
        public bool UsePassive
        {
            get;
            set;
        }
        public bool UseBinary
        {
            get;
            set;
        }
        public string RemotePath
        {
            get
            {
                return this.remotePath;
            }
            set
            {
                string text = "/";
                if (!string.IsNullOrEmpty(value) && value != "/")
                {
                    text = value.TrimStart(new char[]
					{
						'/'
					}).TrimEnd(new char[]
					{
						'/'
					}) + "/";
                }
                this.remotePath = text;
            }
        }
        public string LocalPath
        {
            get;
            set;
        }

        public string CurrentDirectory
        {
            get
            {
                string result = string.Empty;
                string url = this.Host.TrimEnd(new char[]
				{
					'/'
				}) + "/" + this.RemotePath;
                FtpWebRequest ftpWebRequest = this.CreateRequest(url, WebRequestMethods.Ftp.PrintWorkingDirectory);
                using (FtpWebResponse ftpWebResponse = (FtpWebResponse)ftpWebRequest.GetResponse())
                {
                    string statusDescription = ftpWebResponse.StatusDescription;
                    int num = statusDescription.IndexOf('"') + 1;
                    int num2 = statusDescription.LastIndexOf('"');
                    if (num2 >= num)
                    {
                        result = statusDescription.Substring(num, num2 - num);
                    }
                }
                return result;
            }
        }
        public FtpClient(FtpConfig config)
        {
            this.UserId = config.UserName;
            this.Password = config.Password;
            this.Host = "ftp://" + config.IP + ":" + config.Port;
            this.RemotePath = config.RemortDir;
            this.LocalPath = config.LocalDir;
            try
            {
                this.Port = Convert.ToInt32(config.Port);
            }
            catch
            {
                this.Port = 21;
            }
            this.Proxy = null;
            this.EnableSsl = false;
            this.UseBinary = true;
            this.UsePassive = true;
            this.FileMatch = config.FileMatch;
            this.Config = config;
        }
        private FtpWebRequest CreateRequest(string url, string method)
        {
            FtpWebRequest ftpWebRequest = (FtpWebRequest)WebRequest.Create(url);
            ftpWebRequest.Credentials = new NetworkCredential(this.UserId, this.Password);
            ftpWebRequest.Proxy = this.Proxy;
            ftpWebRequest.KeepAlive = false;
            ftpWebRequest.UseBinary = this.UseBinary;
            ftpWebRequest.UsePassive = this.UsePassive;
            ftpWebRequest.EnableSsl = this.EnableSsl;
            ftpWebRequest.Method = method;
            return ftpWebRequest;
        }

        public List<string> GetFileList()
        {
            List<string> list = new List<string>();
            bool matchflag = !string.IsNullOrEmpty(this.FileMatch);

            string url = this.Host.TrimEnd(new char[]
			{
				'/'
			}) + "/" + this.RemotePath;
            if (matchflag)
            {
                url += "/" + this.FileMatch;
            }
            FtpWebRequest ftpWebRequest = this.CreateRequest(url, WebRequestMethods.Ftp.ListDirectory);


            Regex r = new Regex(this.FileMatch);

            using (FtpWebResponse ftpWebResponse = (FtpWebResponse)ftpWebRequest.GetResponse())
            {
                StreamReader streamReader = new StreamReader(ftpWebResponse.GetResponseStream(), Encoding.Default);
                for (string text = streamReader.ReadLine(); text != null; text = streamReader.ReadLine())
                {
                    list.Add(text);
                    //if (matchflag)
                    //{
                    //    list.Add(text);
                    //}
                    //else
                    //{
                    //if (r.IsMatch(text))
                    //{
                    //    list.Add(text);
                    //}
                    //char[] separator = new char[]
                    //{
                    //    ',',
                    //    '，',
                    //    '、'
                    //};
                    //string[] array = strMask.Split(separator);
                    //string[] array2 = array;
                    //for (int i = 0; i < array2.Length; i++)
                    //{
                    //    string text2 = array2[i];
                    //    if (text.Trim().ToUpper().Contains(text2.ToUpper()))
                    //    {
                    //        list.Add(text);
                    //        break;
                    //    }
                    //}
                    //}
                }
            }
            return list;
        }

        public List<string> GetFileDetails()
        {
            List<string> list = new List<string>();
            string url = this.Host.TrimEnd(new char[]
			{
				'/'
			}) + "/" + this.RemotePath;
            FtpWebRequest ftpWebRequest = this.CreateRequest(url, WebRequestMethods.Ftp.ListDirectoryDetails);
            using (FtpWebResponse ftpWebResponse = (FtpWebResponse)ftpWebRequest.GetResponse())
            {
                StreamReader streamReader = new StreamReader(ftpWebResponse.GetResponseStream(), Encoding.Default);
                for (string text = streamReader.ReadLine(); text != null; text = streamReader.ReadLine())
                {
                    list.Add(text);
                }
            }
            return list;
        }
        public int GetDirFileCount()
        {
            return this.GetFileList().Count;
        }
        public bool Upload(FileInfo localFile, string remoteFileName)
        {
            bool result = false;
            if (localFile.Exists)
            {
                string url = this.Host.TrimEnd(new char[]
				{
					'/'
				}) + "/" + this.RemotePath + remoteFileName;
                FtpWebRequest ftpWebRequest = this.CreateRequest(url, WebRequestMethods.Ftp.UploadFile);
                using (Stream requestStream = ftpWebRequest.GetRequestStream())
                {
                    using (FileStream fileStream = localFile.OpenRead())
                    {
                        byte[] array = new byte[4096];
                        for (int i = fileStream.Read(array, 0, array.Length); i > 0; i = fileStream.Read(array, 0, array.Length))
                        {
                            requestStream.Write(array, 0, i);
                        }
                        fileStream.Close();
                        result = true;
                    }
                }
                return result;
            }
            throw new Exception(string.Format("本地文件不存在,文件路径:{0}", localFile.FullName));
        }
        public bool Download(string serverName, string localName)
        {
            bool result = false;
            using (FileStream fileStream = new FileStream(localName, FileMode.OpenOrCreate))
            {
                string url = this.Host.TrimEnd(new char[]
				{
					'/'
				}) + "/" + this.RemotePath + serverName;
                FtpWebRequest ftpWebRequest = this.CreateRequest(url, WebRequestMethods.Ftp.DownloadFile);
                ftpWebRequest.ContentOffset = fileStream.Length;
                using (FtpWebResponse ftpWebResponse = (FtpWebResponse)ftpWebRequest.GetResponse())
                {
                    fileStream.Position = fileStream.Length;
                    byte[] array = new byte[4096];
                    for (int i = ftpWebResponse.GetResponseStream().Read(array, 0, array.Length); i > 0; i = ftpWebResponse.GetResponseStream().Read(array, 0, array.Length))
                    {
                        fileStream.Write(array, 0, i);
                    }
                    ftpWebResponse.GetResponseStream().Close();
                }
                result = true;
            }
            return result;
        }


        /// <summary>
        /// 从Ftp下载具有指定文件前缀的文件，并且将下载的文件不包含在指定的日志文件中
        /// </summary>
        /// <param name="logFile">指定记录已经下载的日志文件</param>
        /// <returns>本次下载的文件列表</returns>
        public List<string> SyncServerFile(string logFile)
        {
            List<string> fileList = this.GetFileList();
            int filenum = 0;
            string[] files = null;//已下载文件
            if (File.Exists(logFile))
            {
                lock (_lock)
                {
                    files = File.ReadAllLines(logFile);
                }
            }
            else
            {
                files = new string[] { "" };
            }

            List<string> downLoadFiles = new List<string>();
            foreach (string file in fileList)
            {
                if (Array.IndexOf(files, file) != -1)
                {
                    continue;
                }
                if (filenum >= this.Config.MaxDownLoadFile)  //限制下载文件数
                {
                    break;
                }

                if (this.Download(file, Path.Combine(this.Config.LocalDir, file)))
                {
                    downLoadFiles.Add(file);
                    filenum++;
                }
            }

            return downLoadFiles;
        }
        public bool Rename(string oldFileName, string newFileName)
        {
            bool result = false;
            string url = this.Host.TrimEnd(new char[]
			{
				'/'
			}) + "/" + this.RemotePath + oldFileName;
            FtpWebRequest ftpWebRequest = this.CreateRequest(url, WebRequestMethods.Ftp.Rename);
            ftpWebRequest.RenameTo = newFileName;
            using ((FtpWebResponse)ftpWebRequest.GetResponse())
            {
                result = true;
            }
            return result;
        }
        public bool DeleteFile(string fileName)
        {
            bool result = false;
            string url = this.Host.TrimEnd(new char[]
			{
				'/'
			}) + "/" + this.RemotePath + fileName;
            FtpWebRequest ftpWebRequest = this.CreateRequest(url, WebRequestMethods.Ftp.DeleteFile);
            using ((FtpWebResponse)ftpWebRequest.GetResponse())
            {
                result = true;
            }
            return result;
        }
        public bool MakeDirectory(string dirName)
        {
            bool result = false;
            string url = this.Host.TrimEnd(new char[]
			{
				'/'
			}) + "/" + this.RemotePath + dirName;
            FtpWebRequest ftpWebRequest = this.CreateRequest(url, WebRequestMethods.Ftp.MakeDirectory);
            using ((FtpWebResponse)ftpWebRequest.GetResponse())
            {
                result = true;
            }
            return result;
        }
        public bool DeleteDirectory(string dirName)
        {
            bool result = false;
            string url = this.Host.TrimEnd(new char[]
			{
				'/'
			}) + "/" + this.RemotePath + dirName;
            FtpWebRequest ftpWebRequest = this.CreateRequest(url, WebRequestMethods.Ftp.RemoveDirectory);
            using ((FtpWebResponse)ftpWebRequest.GetResponse())
            {
                result = true;
            }
            return result;
        }
        public long GetFileSize(string fileName)
        {
            long result = 0L;
            string url = this.Host.TrimEnd(new char[]
			{
				'/'
			}) + "/" + this.RemotePath + fileName;
            FtpWebRequest ftpWebRequest = this.CreateRequest(url, WebRequestMethods.Ftp.GetFileSize);
            using (FtpWebResponse ftpWebResponse = (FtpWebResponse)ftpWebRequest.GetResponse())
            {
                result = ftpWebResponse.ContentLength;
            }
            return result;
        }
        public bool Append(FileInfo localFile, string remoteFileName)
        {
            if (localFile.Exists)
            {
                using (FileStream fileStream = new FileStream(localFile.FullName, FileMode.Open))
                {
                    return this.Append(fileStream, remoteFileName);
                }
            }
            throw new Exception(string.Format("本地文件不存在,文件路径:{0}", localFile.FullName));
        }
        public bool Append(Stream stream, string remoteFileName)
        {
            bool result = false;
            if (stream != null && stream.CanRead)
            {
                string url = this.Host.TrimEnd(new char[]
				{
					'/'
				}) + "/" + this.RemotePath + remoteFileName;
                FtpWebRequest ftpWebRequest = this.CreateRequest(url, WebRequestMethods.Ftp.AppendFile);
                using (Stream requestStream = ftpWebRequest.GetRequestStream())
                {
                    byte[] array = new byte[4096];
                    for (int i = stream.Read(array, 0, array.Length); i > 0; i = stream.Read(array, 0, array.Length))
                    {
                        requestStream.Write(array, 0, i);
                    }
                    result = true;
                }
            }
            return result;
        }
        public bool CheckFileExist(string fileName)
        {
            bool result = false;
            if (fileName != null && fileName.Trim().Length > 0)
            {
                fileName = fileName.Trim();
                List<string> fileList = this.GetFileList();
                if (fileList != null && fileList.Count > 0)
                {
                    foreach (string current in fileList)
                    {
                        if (current.ToLower() == fileName.ToLower())
                        {
                            result = true;
                            break;
                        }
                    }
                }
            }
            return result;
        }
    }

}
